package org.NooLab.randomgraph.components;

import java.sql.Connection;
import java.util.ArrayList;

import org.NooLab.randomgraph.RandomGraph;
import org.NooLab.randomgraph.RandomGraphFactory;
import org.NooLab.randomgraph.RandomGraphIntf;

import org.NooLab.randomgraph.properties.RGraphProperties;
import org.NooLab.randomgraph.storage.RGraphDataBase;
import org.NooLab.randomgraph.storage.RGraphFingerPrint;
import org.NooLab.structures.randomgraph.ParticledText;
import org.NooLab.structures.randomgraph.WordPosition;
import org.NooLab.structures.randomgraph.WordPositions;
import org.NooLab.utilities.datatypes.IndexDistance;
import org.NooLab.utilities.datatypes.IndexedDistances;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.strings.ArrUtilities;
import org.NooLab.utilities.strings.StringsUtil;

import org.math.array.StatisticSample;
import org.math.array.util.Random;



public class RGraphFingerPrints {

	RandomGraphFactory rgFactory ;
	RGraphProperties rgProperties;
	RandomGraph rGraph ;
	RandomGraphIntf irGraph ;
	
	RGraphDataBase rgDatabase;
	Connection connection; 
	
	ArrayList<RGraphFingerPrint> knownFingerprints = new ArrayList<RGraphFingerPrint>();
	
	
	StatisticSample sampler ;
	ArrUtilities arrutil = new ArrUtilities ();
	StringsUtil strgutil = new StringsUtil();
	
	PrintLog out = new PrintLog(2,true);
	// ========================================================================
	public RGraphFingerPrints(RandomGraphFactory factory) {
		rgFactory = factory;
		rgProperties = (RGraphProperties) rgFactory.getRgProperties() ;
		rGraph = RandomGraphFactory.getRGraph( this.getClass() ) ;
		rgProperties = rGraph.getRgProperties() ;
		irGraph = rgFactory.getRGraph();
		out = rGraph.getOut();
		if (out==null){ out= new PrintLog(2,false); }
	}
	// ========================================================================


	public void init() throws Exception {
		// cares for the DB
		rgDatabase = new RGraphDataBase(rgProperties);
		
		rgDatabase.prepareDatabase() ;
		connection = rgDatabase.getConnection() ;
		
		// we should use the length of the fingerprints table for the initialization
		
		String str = String.valueOf( System.currentTimeMillis());
		str = str.substring( str.length()-5,str.length()) ;
		long rx = Long.parseLong(str) ;
		sampler = new StatisticSample(172839 + rx); 
	}



	public RGraphDataBase getRgDatabase(){
		return rgDatabase;
	}



	public boolean checkForDbAvailability() {
		// 
		boolean rB=false;
		
		try{
			if ((connection==null) || (connection.isClosed())){
				rgDatabase.checkForDbAvailability() ;
			
				connection = rgDatabase.getConnection() ;
			}
			if (connection.isClosed()==false){
				rB=true;
				
			}
			
		}catch(Exception e){
		}
		
		
		return rB;
	}
	
	private RGraphFingerPrint checkFingerprintsListForKnown( String wordLabel, String storLabel) {
		RGraphFingerPrint rgfp,fingerprint = null;
		
		try{
			
			for (int i=0;i<knownFingerprints.size();i++){
				
				rgfp = knownFingerprints.get(i) ;
				
				if ((rgfp.getRawlabel().contentEquals(wordLabel)) && 
					(rgfp.getStorlabel().contentEquals(storLabel))){
					
					fingerprint = rgfp;
					break;
				}
				
			}// i->
			
			
		}catch(Exception e){
			
		}
		
		return fingerprint;
	}


	private RGraphFingerPrint createFingerPrint(String wordLabel, String storLabel) {
		
		int fpLen = rgProperties.getLengthFingerprints() ;
		RGraphFingerPrint fp = new RGraphFingerPrint() ;
		double rndVal = 0.0;
		String cLabel,profileStr="" ;
		
		
		ArrayList<Double> profile = new ArrayList<Double>();
		
		/* get a similar one, if available, using 
		 *   -  "like" on shortened string (if it is a long one)
		 *   -  hashkey + LevenShtein 
		 *   -  synonym 
		 *   -  SOM neighborhood, or the vectors from putative place
		 *   
		 * then mix it using a small weight (0.2) into the random values
		 * 
		 *  fingerprints will change anyway over time!!!
		 */
		
		try{
			
			if (storLabel.length()==0){
				storLabel = ".........." ;
			}
			cLabel = wordLabel.trim() + storLabel;
			
			for (int i=0;i<fpLen;i++){
				
				rndVal = sampler.getNextUniformRandom();
				profile.add( rndVal ); 
			}
			profileStr = ArrUtilities.arr2Text(profile, 5, ";");
			
			fp.setFingerprint( profileStr) ;
			fp.setProfile(profile) ;
			fp.setRawlabel(wordLabel) ;
			fp.setStorlabel(storLabel) ;
			fp.setCompoundlabel( cLabel);
			//
			fp.setGuid("") ;
			fp.setUsedhash(0) ;
			fp.setHashkey1(0L) ;
			fp.setHashkey2(0L) ;
			fp.setHashkey3(0L) ;
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return fp ;
	}

	
	class SingleFingerPrint{

		WordPosition cword;
		WordPositions cWords;
		String wordLabel ;
		int wIndex=-1;
		
		RGraphFingerPrint rgFP;
		ArrayList<Double> uProfile = new ArrayList<Double>();
		
		
		public SingleFingerPrint( WordPositions cWords, int w ) {
			this.cWords = cWords;
			cword = cWords.getItem(w);
			wIndex = w;
		}

		public void create() throws Exception {
			
			wordLabel = cword.getWordStr();

			// default value for results of queries to SomStore, if it is not yet available...
			// FIXME: later we need an active method for that !!!
			String storLabel = RandomGraphIntf._FINGERPRINT_STORLABEL_EMPTY ;

			 
			rgFP = checkFingerprintsListForKnown( wordLabel, storLabel );
					 
			
			if ((rgFP==null) || (uProfile.size() == 0)) {
				uProfile = (rgDatabase.getFingerprint(wordLabel, storLabel)).getProfile();
			}else{
				cword.setContextFingerprint(rgFP.getProfile());
			}

			
			// still nothing? -> create a new one
			if (uProfile.size() == 0) {
				
				rgFP = createFingerPrint(wordLabel, storLabel);
					
				// this word and all equal ones, but in case of identical words, we will test 
				// it as soon as the word appears, checking the list "fingerprints"
				cword.setContextFingerprint(rgFP.getProfile());
				knownFingerprints.add(rgFP) ;
				
			} // profile size ? 
			else{
				cword.setContextFingerprint( uProfile );
				
			}

		}
		
	}

	
	/**
	 * this is doing the same as "careForFingerprints", yet, all the db interaction is separated 
	 * from the fingerprint creation, in order to speed things up (hopefully)
	 * 
	 * The cWords are already unique... so we can perform the creation (slow) in parallel
	 * 
	 * @param particledText
	 * @return
	 */
	public boolean careForFingerprintsP(ParticledText particledText) {


		boolean hb,rB = false;
		ArrayList<Double> uProfile = new ArrayList<Double>();
		WordPositions cWords ;
		WordPosition cword ;
		String wordLabel;
		
		RGraphFingerPrint fingerprint ;
		RGraphFingerPrint rgFP ;
		
		
		
		
		knownFingerprints.clear() ;
		
		cWords = particledText.getCollWordPositions();
		
		try {
			// we can't do that in parallel mode, because we write to the database,...
			// or we do it in parallel AFTER making the list unique and AFTER checking the word for presence, both steps should be fast
			// but we do it in the X-version
			if (cWords.size()>0){
				rB = true;
			}else{
				return true;
			}
			
			// cWords should already be unique here , 
			
			
													out.print(2,"caring for fingerprints (n="+cWords.size()+")...");
			for (int w = 0; w < cWords.size(); w++) {
													out.printprc(2, w, cWords.size(), (int) ((double)cWords.size()/(double)10.0), "");

				(new SingleFingerPrint( cWords, w )).create();
				// TODO: this compartmented call we can use in multi digester... 
			} // w->

			// now putting everything to the database at once in a single step
			rgDatabase.storeFingerprints( knownFingerprints );
			
		} catch (Exception e) {
			
			rB = false;
			e.printStackTrace();
		}		
		
		knownFingerprints.clear();
		return rB;
	}
	

	
	public boolean careForFingerprints(ParticledText particledText) {

		boolean hb,rB = false;
		ArrayList<Double> uProfile ;
		WordPositions cWords ;
		WordPosition cword ;
		String wordLabel;
		RGraphFingerPrint rgFP ;
		
		cWords = particledText.getCollWordPositions();
		
		try {
			// we can't do that in parallel mode, because we write to the database,...
			// or we do it in parallel AFTER making the list unique and AFTER checking the word for presence, both steps should be fast
			// but we do it in the X-version
			if (cWords.size()>0){
				rB = true;
			}else{
				return true;
			}
			
			
			
			
													out.print(2,"caring for fingerprints (n="+cWords.size()+")...");
			for (int w = 0; w < cWords.size(); w++) {
													out.printprc(2, w, cWords.size(), (int) ((double)cWords.size()/(double)50.0), "");
				cword = cWords.getItem(w);
				wordLabel = cword.getWordStr();

				// default value for results of queries to SomStore, if it is not yet available...
				// FIXME: later we need an active method for that !!!
				String storLabel = RandomGraphIntf._FINGERPRINT_STORLABEL_EMPTY ;

				uProfile = (rgDatabase.getFingerprint(wordLabel, storLabel)).getProfile();

				if (uProfile.size() == 0) {
					RGraphFingerPrint fingerprint = createFingerPrint( wordLabel, storLabel );
					
					Long upId = rgDatabase.storeFingerprint( fingerprint );
					
					if (upId>=0){
						 
						rgFP = rgDatabase.getFingerprint(wordLabel, storLabel);
						
						hb = rgFP.getProfile().size()>0; 
						
						if (hb==false){
							// log failed words...
							rB=false;
						}else{
							cword.setContextFingerprint(fingerprint.getProfile());
						}
					}
					
				} // profile size ? 
				else{
					cword.setContextFingerprint( uProfile );
				}
			} // w->

			
		} catch (Exception e) {
			
			rB = false;
			e.printStackTrace();
		}		
		return rB;
	}

	
	
	
	
}
